libobs_simple\sources\linux\sources/
linux_general_screen_capture.rs

1use libobs_wrapper::{
2    data::ObsObjectBuilder,
3    runtime::ObsRuntime,
4    sources::ObsSourceRef,
5    utils::{ObsError, SourceInfo},
6};
7use std::env;
8
9use crate::sources::linux::{
10    sources::x11_capture::X11CaptureSourceBuilder, PipeWireDesktopCaptureSourceBuilder,
11};
12
13/// Display server type detection
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum DisplayServerType {
16    /// Wayland display server
17    Wayland,
18    /// X11/Xorg display server
19    X11,
20    /// Unknown or undetected display server
21    Unknown,
22}
23
24impl DisplayServerType {
25    /// Detect the current display server type using environment variables.
26    ///
27    /// Checks in order:
28    /// 1. `XDG_SESSION_TYPE` (most reliable)
29    /// 2. `WAYLAND_DISPLAY` (indicates Wayland)
30    /// 3. `DISPLAY` (indicates X11)
31    pub fn detect() -> Self {
32        // First, check XDG_SESSION_TYPE (most reliable)
33        if let Ok(session_type) = env::var("XDG_SESSION_TYPE") {
34            let session_type = session_type.to_lowercase();
35            if session_type.contains("wayland") {
36                return DisplayServerType::Wayland;
37            } else if session_type.contains("x11") {
38                return DisplayServerType::X11;
39            }
40        }
41
42        // Check WAYLAND_DISPLAY (if set, we're on Wayland)
43        if env::var("WAYLAND_DISPLAY").is_ok() {
44            return DisplayServerType::Wayland;
45        }
46
47        // Check DISPLAY (if set and no Wayland indicators, we're on X11)
48        if env::var("DISPLAY").is_ok() {
49            return DisplayServerType::X11;
50        }
51
52        DisplayServerType::Unknown
53    }
54
55    /// Returns whether PipeWire should be preferred for this display server.
56    ///
57    /// PipeWire is the modern capture API and works on both X11 and Wayland,
58    /// but is essential for Wayland and optional for X11.
59    pub fn prefer_pipewire(&self) -> bool {
60        match self {
61            DisplayServerType::Wayland => true, // PipeWire is required for Wayland
62            DisplayServerType::X11 => false,    // X11 has native capture
63            DisplayServerType::Unknown => true, // Default to PipeWire for safety
64        }
65    }
66}
67
68/// General Linux screen capture source that automatically selects the best capture method.
69///
70/// This wrapper automatically chooses between:
71/// - **PipeWire capture** (for Wayland or modern Linux setups)
72/// - **X11 screen capture** (for traditional X11 setups)
73///
74/// The selection is based on the detected display server type.
75///
76/// # Example
77///
78/// ```no_run
79/// use libobs_simple::sources::linux::LinuxGeneralScreenCapture;
80/// use libobs_wrapper::{context::ObsContext, sources::ObsSourceBuilder, utils::StartupInfo};
81///
82/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
83/// # let startup_info = StartupInfo::default();
84/// # let mut context = ObsContext::new(startup_info)?;
85/// # let mut scene = context.scene("Main Scene")?;
86///
87/// // Automatically selects PipeWire or X11 based on display server
88/// let capture = LinuxGeneralScreenCapture::auto_detect(
89///     &mut context,
90///     "Screen Capture"
91/// )?;
92///
93/// // Add to scene
94/// scene.add(&capture)?;
95/// # Ok(())
96/// # }
97/// ```
98#[derive(Debug)]
99pub struct LinuxGeneralScreenCapture {
100    info: SourceInfo,
101    capture_type: CaptureType,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105enum CaptureType {
106    PipeWire,
107    X11,
108}
109
110impl LinuxGeneralScreenCapture {
111    /// Create a screen capture source by auto-detecting the display server type.
112    ///
113    /// This is the recommended way to create a screen capture on Linux.
114    //TODO rework this API, the best way to use this is to create a SourceBuilder that does the detection internally
115    pub fn auto_detect(
116        runtime: ObsRuntime,
117        name: &str,
118        restore_token: Option<String>,
119    ) -> Result<Self, Box<dyn std::error::Error>> {
120        let display_type = DisplayServerType::detect();
121        Self::new(runtime, name, display_type, restore_token)
122    }
123
124    /// Create a screen capture source for a specific display server type.
125    ///
126    /// # Arguments
127    ///
128    /// * `runtime` - The OBS runtime
129    /// * `name` - Name for the source
130    /// * `display_type` - The display server type to create a source for
131    /// * `restore_token` - Optional restore token for restoring source settings (this is only for pipewire)
132    pub fn new(
133        runtime: ObsRuntime,
134        name: &str,
135        display_type: DisplayServerType,
136        restore_token: Option<String>,
137    ) -> Result<Self, Box<dyn std::error::Error>> {
138        if display_type.prefer_pipewire() {
139            Self::new_pipewire(runtime, name, restore_token)
140        } else {
141            Self::new_x11(runtime, name)
142        }
143    }
144
145    /// Create a PipeWire-based screen capture source.
146    pub fn new_pipewire(
147        runtime: ObsRuntime,
148        name: &str,
149        restore_token: Option<String>,
150    ) -> Result<Self, Box<dyn std::error::Error>> {
151        let builder =
152            PipeWireDesktopCaptureSourceBuilder::new(name, runtime.clone())?.set_show_cursor(true);
153        let builder = if let Some(token) = restore_token {
154            builder.set_restore_token(token)
155        } else {
156            builder
157        };
158
159        let info = builder.build()?;
160        Ok(LinuxGeneralScreenCapture {
161            info,
162            capture_type: CaptureType::PipeWire,
163        })
164    }
165
166    /// Create an X11-based screen capture source.
167    pub fn new_x11(runtime: ObsRuntime, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
168        let builder = X11CaptureSourceBuilder::new(name, runtime.clone())?;
169        let info = builder.set_show_cursor(true).set_screen(0).build()?;
170        Ok(LinuxGeneralScreenCapture {
171            info,
172            capture_type: CaptureType::X11,
173        })
174    }
175
176    pub fn add_to_scene(
177        self,
178        scene: &mut libobs_wrapper::scenes::ObsSceneRef,
179    ) -> Result<ObsSourceRef, ObsError> {
180        scene.add_source(self.info)
181    }
182
183    /// Get the type of capture being used.
184    pub fn capture_type_name(&self) -> &str {
185        match self.capture_type {
186            CaptureType::PipeWire => "PipeWire",
187            CaptureType::X11 => "X11",
188        }
189    }
190}
191
192impl AsRef<SourceInfo> for LinuxGeneralScreenCapture {
193    fn as_ref(&self) -> &SourceInfo {
194        &self.info
195    }
196}